/** \file ActionDispatcher.hpp
*  \author Adrian Schweizer
*  \created  $Sa 18 Aug 03:48:01 pm CEST 2007 schwadri@SchwadriComp.local$
*  \modified $Mo 11 Feb 09:22:52 pm CET 2008 schwadri@SchwadriLaptop.local$
*/

#include "ActionDispatcher.hpp"
#include "GameController.hpp"
#include "../network/NetworkController.hpp"

#include "../core/switch/switch.hpp"
#include "../core/switch/case_range.hpp"
#include "../core/switch/default.hpp"

using namespace game::action;
using namespace game;

struct  Dispatcher::ActionHandler
{
    ActionHandler(Dispatcher& dispatcher, core::bitstream & bin, network::Client::Id source);

    template<class Action>
    void operator()(Action) const;

    Dispatcher&         m_dispatcher;
    core::bitstream &   m_in;
    network::Client::Id m_source;
};

Dispatcher::ActionHandler::ActionHandler(Dispatcher& dispatcher, core::bitstream & bin, network::Client::Id source)
:   m_dispatcher(dispatcher),
    m_in(bin),
    m_source(source)
{ }

//member template method implementations
template<class Case>
    void Dispatcher::ActionHandler::operator()(Case) const//network::Client::Id source)const
    {
        bitstream_iarchive  in(m_in);
        typedef typename boost::mpl::at<GameActions,Case>::type  Action;
        Action action;

        in >> action;

        m_dispatcher.Dispatch(action,m_source);
    }


//===============================================================================
///Dispatcher Implementation
//===============================================================================

Dispatcher::Dispatcher(GameController& gc)
:   m_game_controller(gc)
{ }

bool    Dispatcher::IsServer() const
{
    return m_game_controller.Network().IsServer();
}

void    default_handler(int id)
{
    std::cout << "Invalid Action Id : " << id << std::endl;
}

bool    Dispatcher::Dispatch(core::bitstream& in, network::Client::Id source)
{
    uint8 aid;

    if(!in.read_aligned(aid))
        return false;

    //validate action id

    typedef boost::mpl::size<GameActions>::type    action_id_end;

    if(aid < network::ID_GAME_ACTION_BEGIN || aid>=action_id_end::value)
        return false;

    try {
        core::switch_<void>(aid-network::ID_GAME_ACTION_BEGIN)
        [
            core::case_range_c<uint8,0,boost::mpl::size<GameActions>::value>(ActionHandler(*this,in,source)),
            core::default_(default_handler)
        ];
    }
    catch(const core::bitstream_iarchive_exception&)
    {
        //catch errors during deserialization caused by malformed (too short) bitstreams
        std::cout << "malformed packet"<<std::endl;
        return false;
    }
    /*catch(const core::dispatch::invalid_key<uint8>& e)
    {
        std::cout << e.what() << " : " << e.key << std::endl;
        return false;
    }
    catch(const core::dispatch::unhandled_key<uint8>& e)
    {
        std::cout << e.what() << " : " << e.key << std::endl;
        return false;
    }*/

    //TODO: catch anything else?
    return true;
}

void Dispatcher::Send(core::bitstream& b, network::receiver_mask receivers)
{
//FIXME:m_game_controller.Network().Send(bout,ac.Receivers());
}

/*template<class Action>
    void Dispatcher::Send(const Action & ac)
    {
        core::bitstream  bout;
        bitstream_oarchive  out(bout);
        uint8 id = get_action_id<Action>::value;
        bout.write_aligned(id);
        out << ac;
        //Send to receivers as specified in the receiver mask
        Send(bout,ac.Receivers());
    }

template<class Action>
    void Dispatcher::SendToServer(Action & ac)
    {
        core::bitstream  bout;
        bitstream_oarchive  out(bout);
        uint8 id = get_action_id<Action>::value;
        bout.write_aligned(id);
        out << ac;
        //send to server
        Send(bout,ac.Receivers());
    }

template<class Action>
    void Dispatcher::SendTo(Action & ac, network::Client::Id destination)
    {
        core::bitstream  bout;
        bitstream_oarchive  out(bout);
        bout.write_aligned(get_action_id<Action>::value);
        out << ac;
        //TODO: specify exact interaction with network module
        m_game_controller.Network().SendTo(bout,destination);
    }
*/
